#include "monitor.h"
#include "temu.h"
#include "watchpoint.h"
#include "expr.h"

#include <stdlib.h>
#include <readline/readline.h>
#include <readline/history.h>

void cpu_exec(uint32_t);

uint32_t expr(char* e,bool* seccess);

void display_reg();

/* We use the `readline' library to provide more flexibility to read from stdin. */
char* rl_gets() {
	  char *line_read = NULL;

	if (line_read) {
		free(line_read);
		line_read = NULL;
	}

	line_read = readline("(temu) ");

	if (line_read && *line_read) {
		add_history(line_read);
	}

	return line_read;
}

  int cmd_c(char *args) {
	cpu_exec(-1);
	return 0;
}

  int cmd_q(char *args) {
	exit(0);
	return 0;
}

  int cmd_help(char *args);

  int cmd_info(char* args){
	char* arg;
	arg = strtok(NULL," ");
	if(strcmp(arg,"r")==0){
		display_reg();
		return 0;
	}
	if(strcmp(arg,"w")==0){
		return 0;
	}
	printf("bad command please type 'help' for help\n");
	return -1;
}

int cmd_si(char* args){
	char *arg = strtok(NULL," ");
	if(arg == NULL){
		cpu_exec(1);
		return 0;
	}
	int i;
	sscanf(arg,"%d",&i);
	if(i<-1){
		printf("Parameter error!\n");
		return 0;
	}
	if(i==-1){
		cpu_exec(-1);
	}
	int j = 0;
	for(;j<i;j++){
		cpu_exec(1);
	}
	return 0;
}

int cmd_x(char *args){
	char* arg1 = strtok(NULL," ");
	char* arg2;
	if(arg1 != NULL){
		arg2 = strtok(NULL," ");
	}
	int len;
	uint32_t address;
	sscanf(arg1,"%d",&len);
	sscanf(arg2,"%x",&address);
	//printf("0x%x: ",address); // debug too!
	// 这里，由于实际的机器是小端的，希望可以从零开始打印。
	// 写一个可以按byte打印的函数
	
	/*int i=0;
	for(;i<len;i++){
		printf("0x%x ",mem_read(address,4));
		address+=4;
	}
	printf("\n");*/
	int print_sym = 0;
	for (int i = 0; i < len; i++)
	{
		if(i%16 == 0){
			printf("\n0x%8x:  ", i + address);
		}
		if(i%8 == 0){
			if(print_sym) printf(" || ");
			print_sym = !(print_sym & 0x00000001);
		}
		printf("%2x ", mem_read(address, 1));
		address += 1;
	}
	printf("\n");
	return 0;
}


int cmd_p(char* args){
	bool success = true;
	int i;
	i = expr(args,&success);
	Assert(success,"cmd_p: bad expr");
	printf("%d\n",i);
	return 0;
}
int cmd_w(char *args){
	WP *p;
	bool tmp;
	p = new_wp();
	printf("Watchpoint %d: %s\n",p->NO,args);
	p->val = expr(args,&tmp);
	strcpy(p->str,args);
	if(!tmp)
		Assert(1,"Wrong!\n");
	printf("Value : %d\n",p->val);
	return 0;
}

static int cmd_d(char *args) {
	int num;
	sscanf (args,"%d",&num);
	delete_wp (num);
	return 0;
}


typedef struct {
	char *name;
	char *description;
	int (*handler) (char *);
}Cmd_table;

Cmd_table cmd_table[] = {
	{"help", "Display informations about all supported commands", cmd_help},
	{"c", "Continue the execution of the program", cmd_c},
	{"q", "Exit TEMU", cmd_q},
	{"info", "info register", cmd_info},
	{"si", "step execute", cmd_si},
	{"x", "scan the memory", cmd_x},
	/* TODO: Add more commands */
	{"p", "compute the number", cmd_p},
	{"w", "Stop the program if the result of the watchpoint has changed.", cmd_w},
	{"d", "Delete the Nth watchpoint", cmd_d},

};

#define NR_CMD (sizeof(cmd_table) / sizeof(cmd_table[0]))

  int cmd_help(char *args) {
	/* extract the first argument */
	char *arg = strtok(NULL, " ");
	int i;

	if(arg == NULL) {
		/* no argument given */
		for(i = 0; i < NR_CMD; i ++) {
			printf("%s - %s\n", cmd_table[i].name, cmd_table[i].description);
		}
	}
	else {
		for(i = 0; i < NR_CMD; i ++) {
			if(strcmp(arg, cmd_table[i].name) == 0) {
				printf("%s - %s\n", cmd_table[i].name, cmd_table[i].description);
				return 0;
			}
		}
		printf("Unknown command '%s'\n", arg);
	}
	return 0;
}

void ui_mainloop() {
	printf("=======\n");
	char a[] = "1+1\000\0\0";
	cmd_p(a);
	while(1) {
		char *str = rl_gets();
		char *str_end = str + strlen(str);

		/* extract the first token as the command */
		char *cmd = strtok(str, " ");
		if(cmd == NULL) { continue; }

		/* treat the remaining string as the arguments,
		 * which may need further parsing
		 */
		char *args = cmd + strlen(cmd) + 1;
		if(args >= str_end) {
			args = NULL;
		}

		int i;
		for(i = 0; i < NR_CMD; i ++) {
			if(strcmp(cmd, cmd_table[i].name) == 0) {
				if(cmd_table[i].handler(args) < 0) {
					break;
				}
				break;
			}
		}

		if(i == NR_CMD) { printf("Unknown command '%s'\n", cmd); }
	}
}
